package storage_spec::shared;

BEGIN {
        if(!exists($::ENV{TRUECL_CODE})) {
                die("Missing TRUECL_CODE environment variable.");
        }
        if(!exists($::ENV{TRUECL_LOG})) {
                die("Missing TRUECL_LOG environment variable.");
        }
        if(!exists($::ENV{TRUECL_CFG})) {
                die("Missing TRUECL_CFG environment variable.");
        }
	push @::INC,"$::ENV{TRUECL_CODE}/lib";
}

use clreq;
use cldb;
use clstatdb;
use Sys::Hostname;
use Data::Dumper;
use lvm;
use Config;
use clutils;

my $df_portable=1;
eval "use Filesys::DfPortable";
if($@) {
	$df_portable=0;
}

my $error_msg=undef;

sub get_error_msg {
	return $error_msg;
}

sub new {
my $proto=shift;
my $class=ref($proto) || $proto;
my $self={};

	$self->{TYPE}="Single Image";
	bless($self,$class);
	return $self;
}

sub replicated {
	return 0;
}


sub type {
my $self=shift;
	return $self->{TYPE};
}

# Can define and use a Single image on just one node
sub min_nodes() {
	return 1;
}

# Can used a single image on any of up to 32 nodes
sub max_nodes() {
	return 32;
}

# Can multiple nodes run the application simultaneously?
sub allow_concurrent() {
	return 0;
}

sub repl_ip() {
	return (1,"NONE");
}

#########################################################################
# The below is called via clreqd - and thus must not use clreqd itself!	#
# This assumes that all file systems and logical volumes in use for the	#
# volume group are closed.
#########################################################################

sub run_before_shutdown_on_current($$;$) {
my ($self,$vgname,$lvname)=@_;
my $lvm=new lvm;

	#################################################################
	# For an named lv only we attempt deactivation, but do not	#
	# report a bug if it fails.					#
	#################################################################
	if(defined($lvname)) {
		my ($rc,$r)=$lvm->vg_deactivate(VGNAME=>$vgname);
		return(1,undef);
	}

	my ($rc,$r)=$lvm->vg_deactivate(VGNAME=>$vgname);
	if($rc) {
		return(0,$r);
	}
	return(1,undef);
}

#########################################################################
# The below is called via clreqd - and thus must not use clreqd itself!	#
# So each host will get this called via a storage_spec call for a 	#
# volume group / application.						#
#########################################################################

sub run_before_shutdown_on_non_current($$$;$) {
my ($self,$vgname,$repl_map,$lvname)=@_;
	#################################################################
	# Do not need to do anything on non-current for shared		#
	# storage.							#
	#################################################################
	return (1,undef);
}

sub run_before_start_on_current{
my ($self,$vgname,$allow_stale,$force,$lvname)=@_;
my $lvm=new lvm;

	$force=0 if !defined($force);
	$allow_stale=0 if !defined($allow_stale);
	#################################################################
	# Do not report an activate failure if just doing a single	#
	# volume.							#
	#################################################################
	if(defined($lvname)) {
		my ($rc,$r)=$lvm->vg_activate(VGNAME=>$vgname,FORCE=>$force);
		return (1,undef);
	}
	my ($rc,$r)=$lvm->vg_activate(VGNAME=>$vgname,FORCE=>$force);
	if($rc==1) {
		return (0,$r);
	}
	return (1,undef);
}

sub run_after_start_on_current($$;$) {
my ($self,$vgname,$lvname)=@_;

	return(1,undef);
}

#########################################################################
# For a shared file system we deactivate it on other nodes (if poss)	#
#########################################################################

sub run_before_start_on_non_current($$;$$) {
my ($self,$vgname,$repl_map,$lvname)=@_;
my $lvm=new lvm;

	my ($rc,$r)=$lvm->vg_deactivate(VGNAME=>$vgname);
	return(1,undef);
}

#########################################################################
# For a shared file system we don't need to do anything on the 		#
# non-current nodes after it has been started on current...		#
#########################################################################

sub run_after_start_on_non_current {
my ($self,$vgname,$lvname)=@_;
my $lvm=new lvm;

	return(1,undef);
}

#########################################################################
# Will return (1,undef) - if resources allocated, or (0,errmsg) if not. #
#########################################################################

sub alloc_resources_extras {
	return ();
}

sub alloc_resources($$$$$$;$) {
my ($self,$vgname,$lvname,$appname,$nodelist,$openlocally,%extras)=@_;

	my @N=split(/,/,$nodelist);
	my $clreqdxml=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
	if(! defined($clreqdxml)) {
		return (0,"Unable to load Request Daemon configuration info.", "Reason: " . clreqdxml::error());
	}

	my $reqd=new clreq(hosts => [@N], clreqdxml => $clreqdxml, timeout => 5);
	my $results=$reqd->run(cmd => ["echo"]);

	my @OK=(); my @DEAD=();
	for (keys(%$results)) {
		if($results->{$_} =~ /Alive!/) {
			push @OK,$_;
		} else {
			push @DEAD,$_;
		}
	}

	if(@DEAD) {
		my $x=join(",",@DEAD);
		return(0,"Following hosts are not available to allocate resources for: $x");
	}

	#################################################################
	# Since this is share there is nothing to do really ...,  but	#
	# we generate a storage_shared table, which has the following	#
	# columns;							#
	# name		- application name				#
	# vglv		- vg/lv name					#
	# vischeck	- comma separated list of hosts, currently	#
	# 		- checked as visible on.			#
	#################################################################

	my $cldb=new cldb("$::ENV{TRUECL_CFG}/config.db");
	if(!defined($cldb)) {
		return(0,"Unable to connect to 'config' database.\nReason: " . (cldb::error)[1]);
	}

	my $r=$cldb->exists("table","storage_shared");
	$cldb->disconnect;
	if($r==0) {
		$r="CREATE TABLE storage_shared(name VARCHAR(50),vglv VARCHAR(50),vischeck VARCHAR(200));";
		#########################################################
		# use reqd to create on all hosts if necessary.		#
		#########################################################
		my $results=$reqd->run(cmd => ["perform_adhoc_sql","SQL",$r,"DBTYPE","config"]);
	}

	#################################################################
	# The table exists, so indicate that only this node has 	#
	# actually tested visibilty of the device - used for checking	#
	# later...							#
	#################################################################

	my $h=hostname;
	$r="INSERT OR REPLACE INTO storage_shared (name,vglv,vischeck) VALUES ('$appname','$vgname/$lvname','$h');";
	$results=$reqd->run(cmd => ["perform_adhoc_sql","SQL",$r,"DBTYPE","config"]);
	return(1,undef);
}

#########################################################################
# Will return (1,undef) - if resources removed, or (0,errmsg) if not. #
#########################################################################

sub remove_resources($$$$$$) {
my ($self,$vgname,$lvname,$appname,$nodelist,$openlocally)=@_;

	my @N=split(/,/,$nodelist);
	my $clreqdxml=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
	if(! defined($clreqdxml)) {
		return (0,"Unable to load Request Daemon configuration info.", "Reason: " . clreqdxml::error());
	}

	my $reqd=new clreq(hosts => [@N], clreqdxml => $clreqdxml, timeout => 5);
	my $results=$reqd->run(cmd => ["echo"]);

	my @OK=(); my @DEAD=();
	for (keys(%$results)) {
		if($results->{$_} =~ /Alive!/) {
			push @OK,$_;
		} else {
			push @DEAD,$_;
		}
	}

	if(@DEAD) {
		my $x=join(",",@DEAD);
		return(0,"Following hosts are not available to remove resources from: $x");
	}

	#################################################################
	# Since this is share there is nothing to do really ...,  but	#
	# we remove any entries from the storage_shared table, if	#
	# present.							#
	#################################################################

	my $h=hostname;
	$r="DELETE FROM storage_shared WHERE name = '$appname' AND vglv = '$vgname/$lvname';"; 
	if(defined(&::msg)) {
		::msg("Using the following command to remove storage_shared entry:");
		::msg($r);
	}
	$results=$reqd->run(cmd => ["perform_adhoc_sql","SQL",$r,"DBTYPE","config"]);
	my @F=();
	for (keys(%$results)) {
		if($results->{$_} !~ /^OK/) {
			push @F,$_;
		}
	}
	if(@F) {
		# ::msg(Dumper($results));
		return(0,"Failed to remove entries on hosts: ".join(",",@F));
	}
	return(1,undef);
}

#########################################################################
# The device status can be used to indicate the status of the device on	#
# the specified host. For this storage spec it indicates whether it has	#
# been visisbility checked on the host or not before via the 		#
# storage_shared table information.					#
#									#
# Returns (0,errmsg) - an error has occured.				#
#         (1,undef)  - validated the device is working as expected	#
#		       on the specified host.				#
#########################################################################

sub device_status($$$$$) {
my ($self,$vg,$lv,$appname,$host)=@_;

	return(1,undef);
}

sub local_devname($$$$) {
my ($self,$vg,$lv,$appname)=@_;
my $lvm=new lvm();

	my $os=lc($^O);
	# We need to know the vg type really...
	my @vgnames=$lvm->vg_list();
	my $vg_type=undef;
	for my $cvg_info (@vgnames) {
		my ($cvg,$cvg_type)=split(/\s+/,$cvg_info);
		if($cvg eq $vg) {
			$vg_type=$cvg_type; last;
		}
	}
	# zfs is not /dev type, we just give it the vg/lv back!
	# prefix with ZFS: to ensure block/fsk checks not done.
	if($vg_type eq "ZFSVM") {
		return (1,"ZFS:$vg/$lv","");
	}
	if($os eq "solaris" && -e "/dev/md/$vg/dsk/$lv") {
		return(1,"/dev/md/$vg/dsk/$lv","/dev/md/$vg/rdsk/$lv");
	}
	#################################################################
	# When VxVM is in use we assume that we will be using DMP..	#
	#################################################################

	if(-e "/dev/vx/dsk/$vg/$lv") {
		return(1,"/dev/vx/dsk/$vg/$lv","/dev/vx/rdsk/$vg/$lv");
	} else {
		if($os eq "linux") {
			return(1,"/dev/$vg/$lv","/dev/$vg/$lv");
		}
		return(1,"/dev/$vg/$lv","/dev/$vg/r$lv");
	}
}

#########################################################################
# The shared image type can be be out of sync [only one data copy] -	#
# and hence sync_attempts_supported==0 and hence this routine should	#
# never be called - but is! So always return the details of the logical	#
# volumes, but with a status of 1 for all vols...
#########################################################################

sub sync_status($$$) {
my ($self,$vgname,$appname)=@_;
my @R=();
my $lvm=new lvm;

	my @LVs=$lvm->lv_list(VGNAME=>$vgname);
	if(@LVs) {
		# Get a list of all logical volumes ...
		my $clv;
		for $clv (@LVs) {
			push @R,[$clv,1,0];
		}
		return (1,@R);

	}
	return 1;
}

#########################################################################
# Only a single image, so no sync attempts supported!			#
#########################################################################

sub sync_attempts_supported($) {
my $self=$_[0];

	return 0;
}

#########################################################################
# The below should never be called for this storage type and so		#
# if it is always indicate that the request is successful!!		#
#########################################################################

sub do_sync_attempt($$$$$) {
my ($self,$vgname,$lvname,$appname,$attempt)=@_;

	return (2,undef);
}

#########################################################################
# Indicates that the device in question is currently synched. Can be a 	#
# no-op if necessary, or can update local info - in this case the	#
# storage_drbd1 table in the config database.				#
# (0,errmsg) - error occurred						#
# (1,undef) - OK							#
#########################################################################

sub registered_synched($$$$) {
my ($self,$vgname,$lvname,$appname)=@_;

	return(1,undef);
}

sub registered_unsynched($$$$) {

	return(1,undef);
}

#########################################################################
# The old and the new size are given to check to see if we are		#
# increasing or reducing. If the current size == newsize then no	#
# changes are necessary. The sizes are given in MB.			#
# Since this is shared storage only need to make the change on the	#
# node where the disk group/volume group is active - the other nodes	#
# will pick up the changes when the re-import is performed.		#
#########################################################################

sub resize_lv($$$$$$$) {
my  ($self,$vg,$lv,$appname,$running,$nodelist,$oldsize,$newsize)=@_;
my @other=();
my $h=hostname;

	my $f=0;
	for (split(/,/,$nodelist)) {
		push @other,$_ if $_ ne $h;
		$f=1 if $_ eq $h;
	}

	#################################################################
	# This action must be performed on a node that is suitable	#
	# for hosting the application in question and hence if that is	#
	# not the case then abort with an error now.			#
	#################################################################

	if($f==0) {
		return(6,"Node '$h' is not configured to host application '$appname'.");
	}

	#################################################################
	# For a replicated storage system we would need to check all	#
	# vg/lv references to ensure all were set to the new size,	#
	# and change them if not. For shared storage we only need to	#
	# course is not necessary.					#
	# 								#
	# The resize function is only supported when the cluster is	# 
	# up and running - whether the application is running or not.	#
	#################################################################

	my $clreqdxml=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
	if(! defined($clreqdxml)) {
		return (0,"Unable to load Request Daemon configuration info.", "Reason: " . clreqdxml::error());
	}

	my $reqd=new clreq(hosts => [$h], clreqdxml => $clreqdxml, timeout => 5);
	my $results=$reqd->run(cmd => ["get_master_node"]);

	my $running_node=undef;
	my $master=undef;
	my $clup=0;
	if($results->{$h}=~/^ERROR No local cluster daemon/) {
		return(5,"Node '$h' is not part of a running cluster.");
	}
	if($results->{$h}=~/^OK\s+([a-zA-Z0-9_]+)/) {
		$master=$1;
	} else {
		return(5,"Unexpected result from 'get_master_node' query.");
	}

	#########################################################
	# Since we are running in the cluster we can query	#
	# the local status database to check to see if the	#
	# application in question is currently running.		#
	#########################################################

	my $clstatdb=new clstatdb("$::ENV{TRUECL_CFG}/status.db");
	if(!defined($clstatdb)) {
		return(0,"Unable to connect to 'status' database.\nReason: " . (clstatdb::error)[1]);
	}

	my $r=$clstatdb->get_rows("SELECT * FROM app_status WHERE name = '$appname';");
	$clstatdb->disconnect;
	$clstatdb=undef;
	if(!defined($r) || !defined($r->[0]->{status})) {
		return(5,"Status database on node '$h' missing entry for application '$appname'.");
	}
	if($r->[0]->{status} ne "STOPPED") {
		$running_node=$r->[0]->{host};
	}

	#################################################################
	# If we have a running node we query the size of the vg/lv on	#
	# that node. If not we make a request to the node that is	#
	# running the application for that information.			#
	#################################################################

	if(defined($running_node)) {
		my $reqd=new clreq(hosts => [$running_node], clreqdxml => $clreqdxml, timeout => 5);
		if(!defined($reqd)) {
			return(5,"Unable to query VG/LV config on node '$running_node'.");
		}
		$r=$reqd->run(cmd => ["lvm_lvlist","VGNAME",$vg]);
		if(!defined($r) || !defined($r->{$running_node})) {
			return(5,"No results for lvm_lvlist query from node '$running_node'.");
		}
		my @h=split(/\n/,$r->{$running_node});
		$f=0;
		my $current_size;
		for (@h) {
			my @z=split(/\s+/,$_);
			if($z[0] eq $lv) {
				$current_size=$z[1]; $f=1; last;
			}
		}
		#########################################################
		# Abort if the logical volume in question has not been	#
		# found.						#
		#########################################################
		if($f==0) {
			return(5,"Logical volume '$lv' not found on host '$running_node'!");
		}
		#########################################################
		# If the correct size already then we need to nothing.	#
		#########################################################
		if($current_size==$newsize) {
			return (0,$newsize);
		}
		# The new size is in Kb so we change to MB ready for lvm_lvresize...
		::msg("Running: lvm_lvresize VGNAME $vg LVNAME $lv KBSIZE $newsize");
		$r=$reqd->run(cmd => ["lvm_lvresize","VGNAME",$vg,"LVNAME",$lv,"KBSIZE",$newsize], timeout => 7200);
		if($r->{$running_node} =~ /^OK/) {
			my ($x)=($r->{$running_node} =~ /^OK\s+(\d+)/);
			return(0,$x);
		} elsif($r->{$running_node} =~ /^ERROR/) {
			return(5,$r->{$running_node});
		}
		return(5,"No results returned from 'lvm_lvresize' request on '$running_node'.");
	} else {
		my $cldb=new cldb("$::ENV{TRUECL_CFG}/config.db");
		if(!defined($cldb)) {
			return(5,"Unable to connect to 'config' database.\nReason: " . (cldb::error)[1]);
		}
		my $r=$cldb->get_rows("SELECT * FROM applications WHERE name = '$appname';");
		$cldb->disconnect;
		$cldb=undef;
		if(!defined($r) || !defined($r->[0]->{nodes})) {
			return(5,"Config database on node '$h' missing entry for application '$appname'.");
		}
		my @h=split(/,/,$r->[0]->{nodes});

		# Get a list of currently running nodes...
		#
		my ($ok,$dead)=get_clreqd_status(3);
		my $use_node=undef;
		for my $x (@h) {
			if(grep {$_ eq $x} @$ok) {
				$use_node=$x; last;
			}
		}
		if(!defined($use_node)) {
			return(5,"No running nodes found suitable for application '$appname' found.");
		}

		my $reqd=new clreq(hosts => [$use_node], clreqdxml => $clreqdxml, timeout => 5);
		if(!defined($reqd)) {
			return(5,"Unable to query VG/LV config on node '$use_node'.");
		}
		#########################################################
		# Firstly ensure the vg is activated.			#
		#########################################################
		$r=$reqd->run(cmd => ["lvm_vgactivate","VGNAME",$vg]);
		if(!defined($r) || !defined($r->{$use_node})) {
			return(5,"No results for lvm_vgactivate request from node '$use_node'.");
		}
		if($r->{$use_node} !~ /^OK/) {
			return(5,"Error from lvb_vgactivate: $r->{$use_node}");
		}

		#########################################################
		# Check the lv actually exists.				#
		#########################################################
		$r=$reqd->run(cmd => ["lvm_lvlist","VGNAME",$vg]);
		if(!defined($r) || !defined($r->{$use_node})) {
			return(5,"No results for lvm_lvlist query from node '$use_node'.");
		}
		@h=split(/\n/,$r->{$use_node});
		$f=0;
		my $current_size;
		for (@h) {
			my @z=split(/\s+/,$_);
			if($z[0] eq $lv) {
				$current_size=$z[1]; $f=1; last;
			}
		}
		#########################################################
		# Abort if the logical volume in question has not been	#
		# found.						#
		#########################################################
		if($f==0) {
			return(5,"Logical volume '$lv' not found on host '$use_node'!");
		}
		#########################################################
		# If the correct size already then we need to nothing.	#
		#########################################################
		if($current_size==$newsize) {
			return (0,$newsize);
		}
		#########################################################
		# The new size is in Kb so we change to MB ready for 	#
		# lvm_lvresize...					#
		#########################################################
		$r=$reqd->run(cmd => ["lvm_lvresize","VGNAME",$vg,"LVNAME",$lv,"KBSIZE",$newsize], timeout => 7200);
		if($r->{$use_node} =~ /^OK/) {
			my ($x)=($r->{$use_node} =~ /^OK\s+(\d+)/);
			return(0,$x);
		}
		return(5,"No results returned from 'lvm_lvresize' request on '$use_node'.");
	}
}

sub validate {
my ($self,$application,$vg,$lv,$nlist,$running_on_node)=@_;

	#########################################################################
	# For the nodes in question here we just update the storage_shared	#
	# table since the higher level routine is expected to have validated	#
	# all lv,vgs are same across all nodes.					#
	#########################################################################
	my $clreqdxml=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
	if(! defined($clreqdxml)) {
		return (1,"Unable to load Request Daemon configuration info.", "Reason: " . clreqdxml::error());
	}
	my @N=split(/,/,$nlist);
	my $req=new clreq(hosts => [@N], clreqdxml => $clreqdxml, timeout => 5);
	if(!defined($req)) {
		my ($rc,$msg)=clreq::get_error();
		return(1,"Unable to create request object - aborting.\nRC=$rc\nERROR=$msg");
	}
	my $cmd="UPDATE storage_shared SET vischeck = '$nlist' WHERE name = '$application' AND vglv = '$vg/$lv';";
	my $results=$req->run(cmd => ["perform_adhoc_sql","DBTYPE","config","SQL",$cmd]);
	my @e=();
	for (keys(%$results)) {
		if($results->{$_} !~ /^OK/) {
			push @e,$_;
		}
	}
	if(@e) {
		return(1,"Failed to update status of shared storage on nodes: '" . join(",",@e) . "'.");
	}
	return(0,undef);
}

sub min_sync_interval {
	return 20;
}

sub sync_boundaries {
	return (-1,-1);
}

#################################################################
# Return (2,undef) indicating operation is not supported.	#
#################################################################

sub set_sync_rate($$$$$;$) {
	return(2,undef);
}

#################################################################
# We assume that all OS's support a shared storage type.	#
#################################################################

sub supported_os {
	return(1);
}

#################################################################
# drbd has no pre-checks for the node to validate/import on.	#
#################################################################

sub check_use_state($$$$;$) {
my ($self,$vgname,$appname,$action,$nodelist,$leave_imported)=@_;
my @other=();
my $h=hostname;

	$leave_imported=0 if !defined($leave_imported);
	my @N=split(/,/,$nodelist);
	if($action ne "setup" && $action ne "validate") {
		return(1,"Action '$action' is not supported.");
	}

	my $f=0;
	for (@N) {
		push @other,$_ if $_ ne $h;
		$f=1 if $_ eq $h;
	}

	#################################################################
	# This action must be performed on a node that is suitable	#
	# for hosting the application in question and hence if that is	#
	# not the case then abort with an error now.			#
	#################################################################

	if($f==0) {
		return(6,"Node '$h' is not configured to host application '$appname'.");
	}

	# Get a list of nodes that are currently up...
	my ($rc,$nl)=availnodes($appname);
	if($rc!=1) {
		return(1,"Error: $nl");
	}

	# Take out current node from node list...
	my @N2=();
	for my $cn (@N) {
		push @N2,$cn if $cn ne $h;
	}
	#########################################################################
	# If other nodes are available check the vg is not active there.	#
	#########################################################################
	if(@N2>0) {
		my $clreqdxml=new clreqdxml("$::ENV{TRUECL_CFG}/clreqd.xml");
		if(! defined($clreqdxml)) {
			return (1,"Unable to load Request Daemon configuration info.", "Reason: " . clreqdxml::error());
		}

		my $reqd=new clreq(hosts => [@N2], clreqdxml => $clreqdxml, timeout => 30);
		if(!defined($reqd)) {
			return(1,"Unable to query VG/LV config on nodes " . join(",",@N) . ".");
		}

		#########################################################
		# Firstly ensure the vg is not running on any nodes.	#
		#########################################################
		my $r=$reqd->run(cmd => ["lvm_vglist"]);
		if(!defined($r)) {
			return(5,"No results for lvm_vglist request from nodes " . join(",",@N2) . ".");
		}
		for my $cn (keys %$r) {
			if($r->{$cn} =~ /^ERROR/) {
				if($action eq "validate") {
					return(1,"Error from node '$cn': $r->{$cn}");
				}
			}
			my @I=split(/\n/,$r->{$cn});
			for my $cvg (@I) {
				my ($a,$b)=split(/\s+/,$cvg);
				if($a eq $vgname) {
					return(1,"Volume group '$vgname' is in use on '$cn'!");
				}
			}
		}

		#########################################################
		# Getting here means the vg is not in use any other	#
		# configured nodes...					#
		#########################################################
	}

	# Next steps ...
	# [1] Import disk group if necessary locally...
	# [2] Perform set/validate
	# [3] export/deport if imported and leaveimported==0
	my $have_imported=0;
	my $lvm=new lvm();
	my @VV=$lvm->vg_list();
	if(!grep {$_ eq $vgname} @VV) {
		$have_imported=1;
		my ($rc2,$err2)=$lvm->vg_activate(VGNAME => $vgname, FORE => 1);
		if($rc2!=0) {
			return(1,"Unable to activate/import volume group '$vgname' on '$h'.");
		}
	}
	
	my ($rc3,$err3)=$lvm->check_usable_state(VGNAME=>$vgname,ACTION=>$action);

	#################################################################
	# If we have imported the disk group, and leaveimported==0, 	#
	# then we need to deport it now!				#
	#################################################################
	if($have_imported && ! $leave_imported) {
		my ($rc2,$err2)=$lvm->vg_deactivate(VGNAME => $vgname, FORE => 1);
	}

	#################################################################
	# if rc3==0 then all is fine, otherwise return error string.	#
	#################################################################

	if($rc3==0) {
		return(0,undef);
	}
	return(1,$err3);
}

1;

